using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

namespace com.flajaxian.Dime
{
    internal class DimeHeader
    {
        private Guid _id;
        private byte[] _bytesId;
        private byte[] _bytesType;
        private int _index;
        private DimeHeaderType _type;
        private bool _isFirst;
        private bool _isLast;
        private bool _isChunked;
        private long _length;
        private byte[] _array;


        public DimeHeader(Guid id, int index, DimeHeaderType type, bool isFirst, bool isLast, bool isChunked, long length)
        {
            this._id = id;
            this._bytesId = Encoding.ASCII.GetBytes(String.Concat("uuid:", this._id.ToString()));
            this._bytesType = Encoding.ASCII.GetBytes((type == DimeHeaderType.Xml) ? "http://schemas.xmlsoap.org/soap/envelope/" : "S3Object");
            this._index = index;
            this._type = type;
            this._isFirst = isFirst;
            this._isLast = isLast;
            this._isChunked = (this._isLast)?false:isChunked;
            this._length = length;

            this._array = new byte[12];

            this.FillTheArray();
        }

        private void FillTheArray()
        {
            this._array[0] |= 0x8; // Specifies the version of the DIME message 
            if (this._isFirst)
            {
                this._array[0] |= 0x4;//Specifies that this record is the first record of the message 
            }
            if (this._isLast)
            {
                this._array[0] |= 0x2;//Specifies that this record is the last record of the message 
            }
            if (this._isChunked)
            {
                this._array[0] |= 0x1;//Specifies that the contents of the message have been chunked
            }
            if (this._index == 0)
            {
                if (this._type == DimeHeaderType.Xml)
                {
                    this._array[1] |= 0x20;// TYPE_T ABSOLUTE_URI
                }
                else if (this._type == DimeHeaderType.Attachment)
                {
                    this._array[1] |= 0x10;// TYPE_T MEDIA_TYPE
                }
            }
            if (this._bytesId.Length > 0)
            {
                this._array[4] = (byte)(this._bytesId.Length >> 8);
                this._array[5] = (byte)this._bytesId.Length;
            }
            if (this._bytesType.Length > 0)
            {
                this._array[6] = (byte)(this._bytesType.Length >> 8);
                this._array[7] = (byte)this._bytesType.Length;
            }
            // file size
            this._array[8] =  (byte)(this._length >> 0x18);
            this._array[9] =  (byte)(this._length >> 0x10);
            this._array[10] = (byte)(this._length >> 0x8);
            this._array[11] = (byte)(this._length);
        }
        private static void WritePadding(Stream destinationStream, int lengthToPad)
        {
            int padLength = lengthToPad % 4;
            if (padLength > 0)
            {
                padLength = 4 - padLength;
                byte[] bytes = new byte[padLength];
                destinationStream.Write(bytes, 0, bytes.Length);
            }

        }
        public void WriteToStream(Stream destinationStream)
        {
            destinationStream.Write(this._array, 0, this._array.Length);
            destinationStream.Write(this._bytesId, 0, this._bytesId.Length);
            DimeHeader.WritePadding(destinationStream, this._bytesId.Length);
            destinationStream.Write(this._bytesType, 0, this._bytesType.Length);
            DimeHeader.WritePadding(destinationStream, this._bytesType.Length);
        }
        public void CopySourceStream(Stream sourceStream, byte[] bytes, Stream destinationStream)
        {
            if (sourceStream != null)
            {
                CopyStream(sourceStream, destinationStream, (int)Math.Min(this._length, sourceStream.Length));
                DimeHeader.WritePadding(destinationStream, (int)Math.Min(this._length, sourceStream.Length));
            }
            else if(bytes != null)
            {
                destinationStream.Write(bytes, 0, bytes.Length);
                DimeHeader.WritePadding(destinationStream, bytes.Length);
            }
        }
        internal static void CopyStream(Stream from, Stream to, int length)
        {
            int bufferSize = 0x200;
            int bytesRead = 0;
            long current = 0;
            int size = (int)Math.Min(length, bufferSize);
            byte[] buffer = new byte[size];
            while ((bytesRead = from.Read(buffer, 0, size)) > 0)
            {
                current += bytesRead;
                to.Write(buffer, 0, bytesRead);
                if ((current + size) > length)
                {
                    size %= length;
                }
                if (current >= length)
                {
                    break;
                }
            }
        }
    }
}
